#include "Paladin.h"

#include <iomanip>

#include "lib.h"

#include "ATL.h"

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const unsigned Paladin::nbrCompetences = 13;

const unsigned Paladin::nbrRestrictions = 7;

const unsigned Paladin::ppAlloues = 60;

const vector<CompetenceClasse> Paladin::Comp = list_of
  (CompetenceClasse("Bonus aux JS"               , 10))
  (CompetenceClasse("Cercle de pouvoir"          ,  5))
  (CompetenceClasse("Destrier"                   ,  5))
  (CompetenceClasse("Détection"                  ,  5))
  (CompetenceClasse("Guérison"                   ,  5))
  (CompetenceClasse("Protection contre le mal"   ,  5))
  (CompetenceClasse("Pureté"                     ,  5))
  (CompetenceClasse("Repousser les morts-vivants", 10))
  (CompetenceClasse("Résistance aux charmes"     , 10))
  (CompetenceClasse("Résistance au poison"       , 10))
  (CompetenceClasse("Sorts de prêtres"           , 10))
  (CompetenceClasse("Spécialisation"             , 10))
  (CompetenceClasse("Vertu curative"             , 10));

const vector<CompetenceClasse> Paladin::Restric = list_of
  (CompetenceClasse("Limitation d'armes"                          ,  5))
  (CompetenceClasse("Limitation à la maille"                      ,  5))
  (CompetenceClasse("Limitation au cuir clouté"                   , 10))
  (CompetenceClasse("Sans armure"                                 , 15))
  (CompetenceClasse("Lim : potions, huiles et parchemins"         ,  5))
  (CompetenceClasse("Lim : anneau, de bâton, bâtonnet et baguette",  5))
  (CompetenceClasse("Lim : armes et armures magiques"             ,  5));

const vector< Prerequis > Paladin::Limites = list_of
  (Prerequis(0, 12))
  (Prerequis(2,  9))
  (Prerequis(4, 13))
  (Prerequis(5, 17));

const set< string > Paladin::RacesAdmises = list_of ("Humain");

/** Constructeur usuel.
 * Permet la construction d'un paladin de A à Z, càd on détermine ici
 * les restrictions, puis les compétences.
 *
 * @param[in] xp le nombre de pts d'expérience du Paladin.
 * @param[in] trait les traits du Paladin, le 7ème contient le score de
 * force exceptionnelle, fixé à 0 si non applicable.
 * @param[in,out] PP nombre de points de personnage (PP) à disposition
 * pour la création du Paladin.
 * @param[in] race nom de la race du personnage.
 */
Paladin::Paladin(unsigned xp, const vector< unsigned >& trait,
		 unsigned& PP, const string& race)
  : Classe( xp, Combattants, "Paladin" )
{
  //NomClasse = "Paladin";
  NombrePPsAlloues = ppAlloues;
  PP += NombrePPsAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  blank() << setfill('*') << setw(56) << '*' << endmsg;
  blank() << "Création d'un paladin" << endmsg << endmsg;

  construitClasse( PP, trait, race );
 
  blank() << endmsg << setfill('*') << setw(56) << '*' << endmsg;
  calculNiveau();
  calculNivMag();
}

/** Constructeur simplifié.
 * Permet de construire un Paladin lors de la lecture d'un ficier xml.
 *
 * @param[in] xp le nombre de pts d'expérience du Paladin.
 * @param[in] trait les traits du Paladin, le 7ème contient le score de
 * force exceptionnellw, fixé à 0 si non applicable.
 * @param[in] race nom de la race du personnage.
 */
Paladin::Paladin( unsigned xp )
  : Classe( xp, Combattants, "Paladin" )
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "Paladin::Paladin -> début" << endmsg;

  //NomClasse = "Paladin";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();

  info() << "Niveau " << Niveau << endmsg;

  func() << "Paladin::Paladin -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel( etat_precedent );
}

/** Constructeur pour le mode graphique.
 * Permet de construire une instance de Paladin avec le contenu
 * minimal, le nombre de points d'expériences et les capacités étant
 * déterminées après la déclaration.
 *
 * @param[in] trait les traits du Paladin, le 7ème contient le score de
 * force exceptionnel, fixé à 0 si non applicable.
 * @param[in] UI contrôle, est présent pour des raisons historiques.
 * @param[in] race nom de la race du personnage.
 */
Paladin::Paladin( const bool& UI )
  : Classe(0, Combattants, "Paladin" )
{
  if (!UI)
    { 
      error() << "Création du personnage impossible" << endmsg;
      exit(1);
    }

  //NomClasse = "Paladin";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}

/**
 * Constructeur de "copie", destiné à être utilisé depuis MetaClasse.
 *
 * @param[in] ptr pointeur sur un objet de type Classe (MetaClasse en
 * fait).
 */
Paladin::Paladin(Classe *ptr)
  : Classe( *ptr )
{
  NomClasse = "Paladin";
  NombrePPsAlloues = ppAlloues;

  NombreTotalCompetences  = nbrCompetences;
  NombreTotalRestrictions = nbrRestrictions;
  NombreTotalEnsembles    = 0;
  NombreMaxCompetences    = 0;
  ListesCompetences       = list_of< vector< CompetenceClasse > >( Comp );
  ListeRestrictions       = Restric;
  PrerequisClasse         = list_of< vector< Prerequis > >( Limites );
  RacesAdmisesClasse      = list_of< set< string > >( RacesAdmises );

  calculNiveau();
  calculNivMag();
}


/** 
 * Destructeur, ne fait rien.
 */
Paladin::~Paladin()
{}

void Paladin::calculNiveau()
{
  if (XP < 75000)
    {
      unsigned i;
      for (i = 0; i < 5; i++)
	{
	  if (XP < (unsigned) 2250 * puissance(2, i))
	    break;
	}
      Niveau = i + 1;
    }
  else if (XP < 150000)
    Niveau = 7;
  else if (XP < 300000)
    Niveau = 8;
  else
      Niveau = (XP - 300000) / 300000 + 9;
}

/**
 * Détermine les JS du Paladin. Il faut faire les modifier au cas où le
 * Paladin a la compétence "Bonus aux JS".
 * @param[in] tab pointeur sur le tableau contenant les JS.
 */
void Paladin::js(unsigned *tab) const
{
  *(tab + 0) = 14; // Paralysie, Poison ou Mort magique
  *(tab + 1) = 16; // Bâton, batonnet baguette
  *(tab + 2) = 15; // Pétrification ou Métamorphose
  *(tab + 3) = 17; // Souffles
  *(tab + 4) = 17; // Sorts
  
  unsigned cat;
  if (Niveau <= 2)
    cat = 0;
  else  if (Niveau <= 4)
    cat = 1;
  else  if (Niveau <= 6)
    cat = 3;
  else  if (Niveau <= 8)
    cat = 4;
  else  if (Niveau <= 10)
    cat = 6;
  else  if (Niveau <= 12)
    cat = 7;
  else  if (Niveau <= 14)
    cat = 9;
  else  if (Niveau <= 16)
    cat = 10;
  else
    cat = 11;

  *(tab + 0) -= cat;
  *(tab + 1) -= cat;
  *(tab + 2) -= cat;
  *(tab + 3) -= cat;
  *(tab + 4) -= cat;

  if (cat == 3)
    *(tab + 3) = 13;
  else if (cat == 4)
    *(tab + 3) = 12;
  else if (cat == 4)
     *(tab + 3) = 9;
  else if (cat == 6)
    *(tab + 3) = 8;
  else if (cat == 7)
    *(tab + 3) = 5;
  else if (cat >= 10)
    *(tab + 3) = 4;

  /* 
   * Attention, bonus aux JS !!
   */
  // unsigned taille( tailleCompetences() );
  // bool bonus(false);
  // for (unsigned i(0); i < taille; i++)
  //   {
  //     if (Competences[i] == 10)
  // 	{
  // 	  bonus = true;
  // 	  break;
  // 	}
  //   }

  bool bonus( ( Competences.end() != find( Competences.begin(),
					   Competences.end(), 10 ) ) );

  if (bonus)
    {
      for (unsigned i(0); i < 5; i++)
	*(tab + i) -= 2;
    }
}

// string Paladin::getCompetence(const unsigned& i) const
// {
//   /*Debug::*/empileNiveau( &elementaire );
//   //DebugLevelType etat_precedent(dout.getLevel());
//   //dout.setLevel(elementaire);
//   if (getComp(i) < nbrCompetences)
//     {
//       debug() << "comp " << getComp(i) << " => comp" << endmsg;
//       /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
//       return (Comp[getComp(i)].Comp);
//     }
//   else
//     {
//       debug() << "comp " << getComp(i) << " => res" << endmsg;
//       /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
//       return (Restric[getComp(i)-nbrCompetences].Comp);
//     }
// }

unsigned Paladin::calculeTAc0() const
{
  return 21 - Niveau;
}

/**
 * Le Paladin peut avoir accès à des sorts de prêtre. Il faut donc en
 * premier lieu vérifier s'il possède la compétence nécessaire, pour
 * savoir s'il aura des sorts dès le niveau 4 ou 9.
 */
void Paladin::calculNivMag()
{
  bool sorts_lvl4(false);

  if ( find( Competences.begin(), Competences.end(), 7 ) != Competences.end() )
    sorts_lvl4 = true;

//   for (unsigned i(0); i < getCombien(); i++)
//     {
//       if (getComp(i) == 7)
// 	{
// 	  sorts_lvl4 = true;
// 	  break;
// 	}
//     }
    
  if (sorts_lvl4)
    {
      if (Niveau < 4)
	NiveauMagique = 0;
      else if (Niveau < 13)
	NiveauMagique = (Niveau - 1) / 3;
      else if (Niveau == 13)
	NiveauMagique = 3;
      else
	NiveauMagique = 4;
    }
  else
    {
      if (Niveau < 9)
	NiveauMagique = 0;
      else if (Niveau < 17)
	NiveauMagique = (Niveau - 7) / 2;
      else
	NiveauMagique = 4;
    }
}

/**
 * Les sphères disponibles sont imposées par la classe. Elles sont donc
 * codées "en dur" ici. Ces sphères sont Combat, Divination, Soin et
 * Protection.
 */
void Paladin::ecrireSorts( const string& nom_fichier, const bool& ecrase,
			   const unsigned& ) const
{

  ofstream sortie;
  if (ecrase)
    sortie.open(nom_fichier.c_str());
  else
    sortie.open(nom_fichier.c_str(), ios::app);

  for (unsigned i(1); i <= NiveauMagique; i++)
    sortie << "\\input{Sorts_clerc/Combat_" + ecrireString(i) + ".inc.tex}" << endl;
  for (unsigned i(1); i <= NiveauMagique; i++)
    sortie << "\\input{Sorts_clerc/Divination_" + ecrireString(i) + ".inc.tex}" << endl;
  for (unsigned i(1); i <= NiveauMagique; i++)
    sortie << "\\input{Sorts_clerc/Soin_" + ecrireString(i) + ".inc.tex}" << endl;
  for (unsigned i(1); i <= NiveauMagique; i++)
    sortie << "\\input{Sorts_clerc/Protection_" + ecrireString(i) + ".inc.tex}" << endl;
  sortie.close();
}

void Paladin::sortsParNiveau( vector< unsigned >& sorts,
			      const unsigned& ) const
{
  /*Debug::*/empileNiveau( &fonction );
  func() << "Paladin::sortsParNiveau -> début" << endmsg;

  vector< unsigned > tmp( 4, 0 );

  switch( Niveau )
    {
    case 1:
    case 2:
    case 3:
      break;
    case 4:
    case 5:
      tmp[0] = 1;
      break;
    case 6:
      tmp[0] = 2;
      break;
    case 7:
    case 8:
      tmp[0] = 2;
      tmp[1] = 1;
      break;
    case 9:
      tmp[0] = 2;
      tmp[1] = 2;
      break;
    case 10:
      tmp[0] = 2;
      tmp[1] = 2;
      tmp[2] = 1;
      break;
    case 11:
      tmp[0] = 2;
      tmp[1] = 2;
      tmp[2] = 2;
      break;
    case 12:
    case 13:
      tmp[0] = 3;
      tmp[1] = 2;
      tmp[2] = 2;
      break;
    case 14:
      tmp[0] = 3;
      tmp[1] = 2;
      tmp[2] = 2;
      tmp[3] = 1;
      break;
    case 15:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 2;
      tmp[3] = 1;
      break;
    case 16:
    case 17:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 1;
      break;
    case 18:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 2;
      break;
    case 19:
      tmp[0] = 3;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 3;
      break;
    default:
      tmp[0] = 4;
      tmp[1] = 3;
      tmp[2] = 3;
      tmp[3] = 3;
    }
  
  sorts.assign( tmp.begin(), tmp.end() );
  sorts.resize( NiveauMagique );

  func() << "Paladin::sortsParNiveau -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet de gérer les compétences martiales, càd lesquelles sont
 * accessibles à quel coût.\n
 *
 * Pour un Paladin, comme pour tous les combattants, le coût est de 2
 * pour n'importe quelle arme. De plus, il peut acquérir un groupe
 * restreint pour 4 pp et un groupe large pour 6 pp.
*/
void Paladin::prixAcces(map<string, unsigned>*& Map) const
{
  /*Debug::*/empileNiveau( &fonction );
  //DebugLevelType etat_precedent(dout.getLevel());
  //dout.setLevel(fonction);
  func() << "Paladin::prixAcces -> début" << endmsg;

  map<string, unsigned>::const_iterator mapIt, mapBegin(Map -> begin()),
    mapEnd(Map -> end());

  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
    {
      lfunc() << mapIt -> first << endmsg;
    }
  Map -> operator[]("Paladin") = 2;
  
  func() << "Paladin::prixAcces -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel(etat_precedent);
}

unsigned Paladin::aSpecialisation() const
{
  return find( Competences.begin(), Competences.end(), 11 ) !=
    Competences.end() ? 1 : 0;
}
